package stat

import (
	

	
	
	
	
	
	
	
	
)

// Option represents an option that can be used to configure a stat panel.
type Option func(stat *Stat) error

type ThresholdStep struct {
	Color string
	Value *float64
}

// TextMode controls if name and value is displayed or just name.
type TextMode string

const (
	TextAuto         TextMode = "auto"
	TextValue        TextMode = "value"
	TextName         TextMode = "name"
	TextValueAndName TextMode = "value_and_name"
	TextNone         TextMode = "none"
)

// OrientationMode controls the layout.
type OrientationMode string

const (
	OrientationAuto       OrientationMode = ""
	OrientationHorizontal OrientationMode = "horizontal"
	OrientationVertical   OrientationMode = "vertical"
)

// ReductionType lets you set the function that your entire query is reduced into a
// single value with.
type ReductionType int

const (
	// Min displays the smallest value of the series.
	Min ReductionType = iota
	// Max displays the largest value of the series.
	Max
	// Avg displays the average of the series.
	Avg

	// First displays the first value of the series.
	First
	// FirstNonNull displays the first non-null value of the series.
	FirstNonNull
	// Last displays the last value of the series.
	Last
	// LastNonNull displays the last non-null value of the series.
	LastNonNull

	// Total displays the sum of values in the series.
	Total
	// Count displays the number of value in the series.
	Count
	// Range displays the difference between the minimum and maximum values.
	Range
)

// ValueMap allows to map a value into explicit text.
type ValueMap struct {
	Value string
	Text  string
}

// RangeMap allows to map a range of values into explicit text.
type RangeMap struct {
	From string
	To   string
	Text string
}

// Stat represents a stat panel.
type Stat struct {
	Builder *sdk.Panel
}

// New creates a new stat panel.
func ( string,  ...Option) (*Stat, error) {
	 := &Stat{Builder: sdk.NewStat()}

	.Builder.IsNew = false

	for ,  := range append(defaults(), ...) {
		if  := ();  != nil {
			return nil, 
		}
	}

	return , nil
}

func defaults() []Option {
	return []Option{
		Text(TextValue),
		ColorValue(),
		Orientation(OrientationVertical),
		Span(6),
		ValueType(Last),
		NoValue("N/A"),
		ColorScheme(scheme.ThresholdsValue(scheme.Last)),
	}
}

// Links adds links to be displayed on this panel.
func ( ...links.Link) Option {
	return func( *Stat) error {
		.Builder.Links = make([]sdk.Link, 0, len())

		for ,  := range  {
			.Builder.Links = append(.Builder.Links, .Builder)
		}

		return nil
	}
}

// DataSource sets the data source to be used by the panel.
func ( string) Option {
	return func( *Stat) error {
		.Builder.Datasource = &sdk.DatasourceRef{LegacyName: }

		return nil
	}
}

// WithPrometheusTarget adds a prometheus query to the graph.
func ( string,  ...prometheus.Option) Option {
	 := prometheus.New(, ...)

	return func( *Stat) error {
		.Builder.AddTarget(&sdk.Target{
			RefID:          .Ref,
			Hide:           .Hidden,
			Expr:           .Expr,
			IntervalFactor: .IntervalFactor,
			Interval:       .Interval,
			Step:           .Step,
			LegendFormat:   .LegendFormat,
			Instant:        .Instant,
			Format:         .Format,
		})

		return nil
	}
}

// WithGraphiteTarget adds a Graphite target to the graph.
func ( string,  ...graphite.Option) Option {
	 := graphite.New(, ...)

	return func( *Stat) error {
		.Builder.AddTarget(.Builder)

		return nil
	}
}

// WithInfluxDBTarget adds an InfluxDB target to the graph.
func ( string,  ...influxdb.Option) Option {
	 := influxdb.New(, ...)

	return func( *Stat) error {
		.Builder.AddTarget(.Builder)

		return nil
	}
}

// WithStackdriverTarget adds a stackdriver query to the graph.
func ( *stackdriver.Stackdriver) Option {
	return func( *Stat) error {
		.Builder.AddTarget(.Builder)

		return nil
	}
}

// Span sets the width of the panel, in grid units. Should be a positive
// number between 1 and 12. Example: 6.
func ( float32) Option {
	return func( *Stat) error {
		if  < 1 ||  > 12 {
			return fmt.Errorf("span must be between 1 and 12: %w", errors.ErrInvalidArgument)
		}

		.Builder.Span = 

		return nil
	}
}

// Height sets the height of the panel, in pixels. Example: "400px".
func ( string) Option {
	return func( *Stat) error {
		.Builder.Height = &

		return nil
	}
}

// Description annotates the current visualization with a human-readable description.
func ( string) Option {
	return func( *Stat) error {
		.Builder.Description = &

		return nil
	}
}

// Transparent makes the background transparent.
func () Option {
	return func( *Stat) error {
		.Builder.Transparent = true

		return nil
	}
}

// Unit sets the unit of the data displayed on this axis.
func ( string) Option {
	return func( *Stat) error {
		.Builder.StatPanel.FieldConfig.Defaults.Unit = 

		return nil
	}
}

// Decimals sets the number of decimals that should be displayed.
func ( int) Option {
	return func( *Stat) error {
		if  < 0 {
			return fmt.Errorf("decimals must be greater than 0: %w", errors.ErrInvalidArgument)
		}

		.Builder.StatPanel.FieldConfig.Defaults.Decimals = &

		return nil
	}
}

// SparkLine displays the spark line summary of the series in addition to the
// stat.
func () Option {
	return func( *Stat) error {
		.Builder.StatPanel.Options.GraphMode = "area"

		return nil
	}
}

// SparkLineYMin defines the smallest value expected on the Y axis of the spark line.
func ( float64) Option {
	return func( *Stat) error {
		.Builder.StatPanel.FieldConfig.Defaults.Min = &

		return nil
	}
}

// SparkLineYMax defines the largest value expected on the Y axis of the spark line.
func ( float64) Option {
	return func( *Stat) error {
		.Builder.StatPanel.FieldConfig.Defaults.Max = &

		return nil
	}
}

// ValueType configures how the series will be reduced to a single value.
func ( ReductionType) Option {
	return func( *Stat) error {
		var  string

		switch  {
		case First:
			 = "first"
		case FirstNonNull:
			 = "firstNotNull"
		case Last:
			 = "last"
		case LastNonNull:
			 = "lastNotNull"

		case Min:
			 = "min"
		case Max:
			 = "max"
		case Avg:
			 = "mean"

		case Count:
			 = "count"
		case Total:
			 = "sum"
		case Range:
			 = "range"

		default:
			return fmt.Errorf("unknown value type: %w", errors.ErrInvalidArgument)
		}
		.Builder.StatPanel.Options.ReduceOptions.Calcs = []string{}

		return nil
	}
}

// ValueFontSize sets the font size used to display the value.
func ( int) Option {
	return func( *Stat) error {
		.Builder.StatPanel.Options.Text.ValueSize = 

		return nil
	}
}

// TitleFontSize sets the font size used to display the title.
func ( int) Option {
	return func( *Stat) error {
		.Builder.StatPanel.Options.Text.TitleSize = 

		return nil
	}
}

// ColorNone will not color the value or the background.
func () Option {
	return func( *Stat) error {
		.Builder.StatPanel.Options.ColorMode = "none"

		return nil
	}
}

// ColorValue will show the threshold's colors on the value itself.
func () Option {
	return func( *Stat) error {
		.Builder.StatPanel.Options.ColorMode = "value"

		return nil
	}
}

// ColorBackground will show the threshold's colors in the background.
func () Option {
	return func( *Stat) error {
		.Builder.StatPanel.Options.ColorMode = "background"

		return nil
	}
}

// AbsoluteThresholds changes the background and value colors dynamically within the
// panel, depending on the value. The threshold is defined by a series of steps
// values which, each having a value and an associated color.
func ( []ThresholdStep) Option {
	return func( *Stat) error {
		 := make([]sdk.ThresholdStep, 0, len())
		for ,  := range  {
			 = append(, sdk.ThresholdStep{
				Color: .Color,
				Value: .Value,
			})
		}

		.Builder.StatPanel.FieldConfig.Defaults.Thresholds = sdk.Thresholds{
			Mode:  "absolute",
			Steps: ,
		}

		return nil
	}
}

// RelativeThresholds changes the background and value colors dynamically within the
// panel, depending on the value. The threshold is defined by a series of steps
// values which, each having a value defined as a percentage and an associated color.
func ( []ThresholdStep) Option {
	return func( *Stat) error {
		 := make([]sdk.ThresholdStep, 0, len())
		for ,  := range  {
			 = append(, sdk.ThresholdStep{
				Color: .Color,
				Value: .Value,
			})
		}

		.Builder.StatPanel.FieldConfig.Defaults.Thresholds = sdk.Thresholds{
			Mode:  "percentage",
			Steps: ,
		}

		return nil
	}
}

// Repeat configures repeating a panel for a variable
func ( string) Option {
	return func( *Stat) error {
		.Builder.Repeat = &

		return nil
	}
}

// RepeatDirection configures repeating vertical or horizontal
func ( sdk.RepeatDirection) Option {
	return func( *Stat) error {
		.Builder.RepeatDirection = &

		return nil
	}
}

// Text indicates if name and value is displayed or just name.
func ( TextMode) Option {
	return func( *Stat) error {
		.Builder.StatPanel.Options.TextMode = string()

		return nil
	}
}

// Orientation changes the orientation of the layout.
func ( OrientationMode) Option {
	return func( *Stat) error {
		.Builder.StatPanel.Options.Orientation = string()

		return nil
	}
}

// ColorScheme configures the color scheme.
func ( ...scheme.Option) Option {
	return func( *Stat) error {
		scheme.New(&.Builder.StatPanel.FieldConfig, ...)

		return nil
	}
}

// NoValue defines what to show when there is no value.
func ( string) Option {
	return func( *Stat) error {
		.Builder.StatPanel.FieldConfig.Defaults.NoValue = 

		return nil
	}
}